package org.jeecg.modules.activiti.web;

import org.activiti.engine.*;
import org.activiti.engine.impl.RepositoryServiceImpl;
import org.activiti.engine.impl.bpmn.behavior.UserTaskActivityBehavior;
import org.activiti.engine.impl.javax.el.ExpressionFactory;
import org.activiti.engine.impl.javax.el.ValueExpression;
import org.activiti.engine.impl.juel.ExpressionFactoryImpl;
import org.activiti.engine.impl.juel.SimpleContext;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.pvm.PvmActivity;
import org.activiti.engine.impl.pvm.PvmTransition;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.impl.task.TaskDefinition;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.Execution;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class TaskUtil {
    @Autowired
    private TaskService taskService;
    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private ManagementService managementService;


    /**
     * 获取下一个用户任务信息
     * @param taskId 任务Id信息
     * @return 下一个用户任务用户组信息
     * @throws Exception
     */
    public TaskDefinition getNextTaskInfo(String taskId) throws Exception {

        ProcessDefinitionEntity processDefinitionEntity = null;

        String id = null;

        TaskDefinition task = null;

        //获取流程实例Id信息

        String processInstanceId = taskService.createTaskQuery().taskId(taskId).singleResult().getProcessInstanceId();

        //获取流程发布Id信息

        String definitionId = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult().getProcessDefinitionId();

        processDefinitionEntity = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService)

                .getDeployedProcessDefinition(definitionId);

        ExecutionEntity execution = (ExecutionEntity) runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();

        //当前流程节点Id信息

        String activitiId = execution.getActivityId();

        //获取流程所有节点信息

        List<ActivityImpl> activitiList = processDefinitionEntity.getActivities();

        //遍历所有节点信息

        for(ActivityImpl activityImpl : activitiList){

            id = activityImpl.getId();

            if (activitiId.equals(id)) {

                //获取下一个节点信息

                task = nextTaskDefinition(activityImpl, activityImpl.getId(), null, processInstanceId);

                break;

            }

        }

        return task;

    }

    /**

     * 下一个任务节点信息,
     * 如果下一个节点为用户任务则直接返回,
     * 如果下一个节点为排他网关, 获取排他网关Id信息, 根据排他网关Id信息和execution获取流程实例排他网关Id为key的变量值,
     * 根据变量值分别执行排他网关后线路中的el表达式, 并找到el表达式通过的线路后的用户任务
     * @param activityImpl    流程节点信息
     * @param activityId            当前流程节点Id信息
     * @param elString              排他网关顺序流线段判断条件
     * @param processInstanceId      流程实例Id信息
     * @return

     */

    private TaskDefinition nextTaskDefinition(ActivityImpl activityImpl, String activityId, String elString, String processInstanceId){

        PvmActivity ac = null;

        Object s = null;

        // 如果遍历节点为用户任务并且节点不是当前节点信息

        if ("userTask".equals(activityImpl.getProperty("type")) && !activityId.equals(activityImpl.getId())) {

            // 获取该节点下一个节点信息

            TaskDefinition taskDefinition = ((UserTaskActivityBehavior) activityImpl.getActivityBehavior())

                    .getTaskDefinition();

            return taskDefinition;

        } else {

            // 获取节点所有流向线路信息

            List<PvmTransition> outTransitions = activityImpl.getOutgoingTransitions();

            List<PvmTransition> outTransitionsTemp = null;

            for (PvmTransition tr : outTransitions) {

                ac = tr.getDestination(); // 获取线路的终点节点

                // 如果流向线路为排他网关

                if ("exclusiveGateway".equals(ac.getProperty("type"))) {

                    outTransitionsTemp = ac.getOutgoingTransitions();

                    // 如果网关路线判断条件为空信息

                    if (StringUtils.isEmpty(elString)) {

                        // 获取流程启动时设置的网关判断条件信息

                        elString = getGatewayCondition(ac.getId(), processInstanceId);

                    }

                    // 如果排他网关只有一条线路信息

                    if (outTransitionsTemp.size() == 1) {

                        return nextTaskDefinition((ActivityImpl) outTransitionsTemp.get(0).getDestination(), activityId,

                                elString, processInstanceId);

                    } else if (outTransitionsTemp.size() > 1) { // 如果排他网关有多条线路信息

                        for (PvmTransition tr1 : outTransitionsTemp) {

                            s = tr1.getProperty("conditionText"); // 获取排他网关线路判断条件信息

                            // 判断el表达式是否成立

                            if (isCondition(ac.getId(), StringUtils.trim(s.toString()), elString)) {

                                return nextTaskDefinition((ActivityImpl) tr1.getDestination(), activityId, elString,

                                        processInstanceId);

                            }

                        }

                    }

                } else if ("userTask".equals(ac.getProperty("type"))) {

                    return ((UserTaskActivityBehavior) ((ActivityImpl) ac).getActivityBehavior()).getTaskDefinition();

                } else {

                }

            }

            return null;

        }

    }

    /**

     * 查询流程启动时设置排他网关判断条件信息

     * @param gatewayId          排他网关Id信息, 流程启动时设置网关路线判断条件key为网关Id信息

     * @param processInstanceId  流程实例Id信息

     * @return

     */

    public String getGatewayCondition(String gatewayId, String processInstanceId) {

        Execution execution = runtimeService.createExecutionQuery().processInstanceId(processInstanceId).singleResult();

        Object object= runtimeService.getVariable(execution.getId(), gatewayId);

        return object==null? "":object.toString();

    }

    /**

     * 根据key和value判断el表达式是否通过信息

     * @param key    el表达式key信息

     * @param el    el表达式信息

     * @param value  el表达式传入值信息

     * @return

     */

    public boolean isCondition(String key, String el, String value) {

        ExpressionFactory factory = new ExpressionFactoryImpl();

        SimpleContext context = new SimpleContext();

        context.setVariable(key, factory.createValueExpression(value, String.class));

        ValueExpression e = factory.createValueExpression(context, el, boolean.class);

        return (Boolean) e.getValue(context);

    }

    /**
     * 流程定义文件下载位置
     */
    private static final String LOCAL_PATH = "D:/";
    /**
     * 流程定义key
     */
    private static final String DEFINITION_KEY = "leave3_";
   /* public static void download(){
        // 得到流程引擎
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        // 得到储存对象service
        RepositoryService repositoryService = processEngine.getRepositoryService();
        // 得到查询器,流程定义查询对象
        // 设置查询条件
        List<ProcessDefinition> definitions = repositoryService.createProcessDefinitionQuery()
                // 设置流程定义Id
                .processDefinitionKey(DEFINITION_KEY)
                // 根据流程定义的版本号进行排序
                .orderByProcessDefinitionVersion()
                // 降序排列
                .desc()
                // 查询列表
                .list();
        // 执行查询操作,查询出想要的流程定义
//        log.error("流程部署个数:{}", definitions.size());
        definitions.forEach(definition -> {
            InputStream bpmnInputStream = null;
            InputStream pngInputStream = null;
            BufferedOutputStream bpmnOutStream = null;
            BufferedOutputStream pngOutStream = null;
            try {
                // 通过流程定义信息,获取流程部署ID
                String deploymentId = definition.getDeploymentId();
                // 获取资源信息bpmn
                String diagramResourceName = definition.getDiagramResourceName();
                // 获取资源信息png
                String resourceName = definition.getResourceName();
                // 通过service的方法实现读取图片的bpmn的信息
                bpmnInputStream = repositoryService.getResourceAsStream(deploymentId, diagramResourceName);
                pngInputStream = repositoryService.getResourceAsStream(deploymentId, resourceName);
                // 构造流,进行输出
                bpmnOutStream = new BufferedOutputStream(new FileOutputStream(LOCAL_PATH + diagramResourceName));
                pngOutStream = new BufferedOutputStream(new FileOutputStream(LOCAL_PATH + resourceName));
                IOUtils.copy(bpmnInputStream, bpmnOutStream);
                IOUtils.copy(pngInputStream, pngOutStream);
            } catch (IOException e) {
//                log.error("错误信息为:{}", e.getMessage());
//                log.error("{}", e);
            } finally {
                try {
                    if (bpmnInputStream != null) {
                        bpmnInputStream.close();
                    }
                    if (pngOutStream != null) {
                        pngOutStream.close();
                    }
                    if (pngInputStream != null) {
                        pngInputStream.close();
                    }
                    if (bpmnOutStream != null) {
                        bpmnOutStream.close();
                    }
                } catch (IOException e) {
//                    log.error("错误信息为:{}", e.getMessage());
//                    log.error("{}", e);
                }
            }
        });
    }*/

    public static Boolean checkFormDataByRuleEl(String el, Map<String, Object> formData) throws Exception {

        ExpressionFactory factory = new ExpressionFactoryImpl();
        SimpleContext context = new SimpleContext();
        for (Object k : formData.keySet()) {
            if (formData.get(k) != null) {
                context.setVariable(k.toString(), factory.createValueExpression(formData.get(k), formData.get(k).getClass()));
            }
        }

        ValueExpression e = factory.createValueExpression(context, el, Boolean.class);
        return (Boolean) e.getValue(context);
    }

    public static void main(String[] args) throws Exception {
        String el = "${请假天数>3 && 部门 == '产品部'}";
        Map<String, Object> formData = new HashMap<>();
        formData.put("请假天数", 4);
        formData.put("部门", "产品部");
        formData.put("test", "123");
        System.out.println(checkFormDataByRuleEl(el, formData));
    }
}
